home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / c / etc / ttyDriver.c < prev    next >
C/C++ Source or Header  |  1992-03-18  |  54KB  |  1,990 lines

  1. /* 
  2.  * ttyDriver.c --
  3.  *
  4.  *    The routines in this module implement an emulator for the
  5.  *    UNIX 4.2 BSD tty driver.  The emulation is done in a way
  6.  *    that is independent of the specific environme (kernel, user,
  7.  *    etc.) by using a set of callback procedures to interface to
  8.  *    a raw device on one side and a client on the "cooked" side..
  9.  *
  10.  * Copyright 1987, 1989 Regents of the University of California
  11.  * Permission to use, copy, modify, and distribute this
  12.  * software and its documentation for any purpose and without
  13.  * fee is hereby granted, provided that the above copyright
  14.  * notice appear in all copies.  The University of California
  15.  * makes no representations about the suitability of this
  16.  * software for any purpose.  It is provided "as is" without
  17.  * express or implied warranty.
  18.  */
  19.  
  20. #ifndef lint
  21. static char rcsid[] = "$Header: /sprite/src/lib/c/etc/RCS/ttyDriver.c,v 1.20 92/03/18 16:33:08 rab Exp $ SPRITE (Berkeley)";
  22. #endif not lint
  23.  
  24. #include <sprite.h>
  25. #include <dev/tty.h>
  26. #include <fs.h>
  27. #include <stdio.h>
  28. #include <ctype.h>
  29. #include <errno.h>
  30. #include <signal.h>
  31. #include <stdlib.h>
  32. #include <sys/ioctl.h>
  33. #include <fmt.h>
  34. #include "td.h"
  35.  
  36. /*
  37.  * The structure below corresponds to one terminal (one call to Td_Create).
  38.  */
  39.  
  40. typedef struct Terminal {
  41.     /*
  42.      * Controlling parameters for terminal.  These all have exactly the
  43.      * same meanings as in 4.3 BSD;  see the 4.3 BSD terminal driver
  44.      * documentation for details.
  45.      */
  46.  
  47.     struct sgttyb sgttyb;
  48.     struct tchars tchars;
  49.     struct ltchars ltchars;
  50.     int localMode;
  51.     struct winsize winsize;
  52.  
  53.     /*
  54.      * State of the terminal.
  55.      */
  56.  
  57.     int column;            /* Column where the next character will be
  58.                  * echoed (i.e., cursor position).  Needed
  59.                  * in order to handle tabs correctly. */
  60.     int owner;            /* Identifier of controlling process or process
  61.                  * group (signals are sent here). */
  62.     int numOpens;        /* Number of opens that have been completed
  63.                  * successfully but not yet closed. */
  64.     int flags;            /* See below for definitions. */
  65.  
  66.     /*
  67.      * Input buffer, indexed circularly.  The buffer is reallocated
  68.      * with a larger size if it ever fills up.
  69.      */
  70.  
  71.     char *inputBuffer;        /* Input buffer area (malloc'ed). */
  72.     int inBufSize;        /* Number of bytes in inputBuffer. */
  73.     int lastAddedIn;        /* Index of last character added to buffer. */
  74.     int lastRemovedIn;        /* Index of last place from which character
  75.                  * was removed from buffer.  If lastAddedIn =
  76.                  * lastRemovedIn, the buffer is empty. */
  77.     int lastBreak;        /* Index of last break character (newline,
  78.                  * etc.) or lastRemovedIn if none in buffer. */
  79.     int lastHidden;        /* Value of lastAddedIn at the time the last
  80.                  * output to the terminal was done.  Characters
  81.                  * before this can't be smoothly backspaced
  82.                  * over, because there's output in front of
  83.                  * them on the screen.  -1 means there are no
  84.                  * editable characters in the buffer that are
  85.                  * hidden. */
  86.  
  87.     /*
  88.      * In order to backspace correctly over weird things like tabs, the
  89.      * following two variables keep track of an index position in the input
  90.      * buffer, and the column just to the right of where that character was
  91.      * echoed on the display.  KeyIndex is either the same as lastBreak or
  92.      * lastHidden or lastRemovedIn, and has two properties:  a) it will
  93.      * never be backspaced over in CRT mode;  and b) no characters beyond
  94.      * this one will be returned to the application until keyIndex is first
  95.      * advanced.
  96.      */
  97.  
  98.     int keyIndex;
  99.     int keyColumn;        /* Column just after keyIndex character. */
  100.  
  101.     /*
  102.      * Output buffer, indexed circularly.  This buffer also grows
  103.      * dynamically if necessary (this is necessary so that new room can
  104.      * be made for characters being echoed, even if the buffer was
  105.      * previously "full").  However, the cooked side of the terminal
  106.      * is marked "not ready for output" whenever there are more than
  107.      * cookedOutputLimit characters in the buffer (but any call to
  108.      * Td_PutCooked will always complete;  it is up to the cooked-side
  109.      * callbacks to stop calling Td_PutCooked).  The goal here is to
  110.      * keep the application from getting too many characters ahead of
  111.      * the actual device.
  112.      */
  113.  
  114.     char *outputBuffer;        /* Output buffer area (malloc'ed). */
  115.     int outBufSize;        /* Number of bytes in outputBuffer. */
  116.     int lastAddedOut;        /* Index of last character added to
  117.                  * outputBuffer. */
  118.     int lastRemovedOut;        /* Index of last character removed from
  119.                  * outputBufer. */
  120.     int outCharsBuffered;    /* Number of characters bufferred in
  121.                  * outputBuffer. */
  122.     int cookedOutputLimit;    /* Mark cooked side not ready for output
  123.                  * whenever outCharsBuffered is greater
  124.                  * than this. */
  125.  
  126.     /*
  127.      * Callback procedures and data provided by the client:
  128.      */
  129.  
  130.     int (*cookedProc)_ARGS_((ClientData, int operation, int inBufSize, 
  131.                  char *inBuffer, int outBufSize,
  132.                  char *outBuffer)); 
  133.                 /* Procedure to call to register change
  134.                  * in state of cooked-side interface. */
  135.     ClientData cookedData;    /* Value to pass to cookedProc. */
  136.     int (*rawProc)_ARGS_((ClientData, int operation, int inBufSize,
  137.               char *inBuffer, int outBufSize,
  138.               char *outBuffer));
  139.                 /* Procedure to call to register change
  140.                  * in state of raw-side interface. */
  141.     ClientData rawData;        /* Value to pass to rawProc. */
  142. } Terminal;
  143.  
  144. /* Flag values:
  145.  *
  146.  * EXCLUSIVE:        No more opens should be allowed until terminal
  147.  *            has been completely closed.
  148.  * BS_IN_PROGRESS:    A printing backspace sequence (delimited by
  149.  *            "\" and "/") is in progress, and will eventually
  150.  *            need the closing "/".
  151.  * LITERAL_NEXT:    The next character should be taken literally,
  152.  *            and should be put into the input buffer with
  153.  *            no special interpretation.
  154.  * OWNER_FAMILY:    1 means the owner is a process family.  0 means
  155.  *            it's a single process.
  156.  * OUTPUT_OFF:        1 means output to the raw device has been stopped,
  157.  *            for example because ^S was typed.
  158.  */
  159.  
  160. #define EXCLUSIVE        0x1
  161. #define BS_IN_PROGRESS        0x2
  162. #define LITERAL_NEXT        0x4
  163. #define OWNER_FAMILY        0x8
  164. #define OUTPUT_OFF        0x10
  165.  
  166. /*
  167.  * Default values for tty parameters:
  168.  */
  169.  
  170. struct sgttyb sgttybDefault = {
  171.     B9600, B9600, 010, 025, EVENP|ODDP|CRMOD|ECHO
  172. };
  173. struct tchars tcharsDefault = {
  174.     03, 034, 021, 023, 04, -1
  175. };
  176. struct ltchars ltcharsDefault = {
  177.     032, 031, 022, 017, 027, 026
  178. };
  179. int localModeDefault = LCRTBS|LCRTERA|LCRTKIL|LCTLECH;
  180. struct winsize winsizeDefault = {
  181.     0, 0, 0, 0
  182. };
  183.  
  184. /*
  185.  * Macros for moving buffer pointers forward and backward circularly.
  186.  */
  187.  
  188. #define NEXT(src, size, dst)         \
  189.     (dst) = (src)+1;            \
  190.     if ((dst) >= (size)) {        \
  191.     (dst) = 0;            \
  192.     }
  193.  
  194. #define PREV(src, size, dst)        \
  195.     (dst) = (src)-1;            \
  196.     if ((dst) < 0) {            \
  197.     (dst) = (size)-1;            \
  198.     }
  199.  
  200. int td_Debug = 0;
  201.  
  202. /*
  203.  * Forward declarations for procedures defined later in this file:
  204.  */
  205.  
  206. static void    TdBackspace _ARGS_((Terminal *tPtr));
  207. static void    TdEcho _ARGS_((Terminal *tPtr, int c));
  208. static void    TdFlushInput _ARGS_((Terminal *tPtr));
  209. static void    TdFlushOutput _ARGS_((Terminal *tPtr));
  210. static void    TdPutChar _ARGS_((Terminal *tPtr, int c));
  211. static void    TdRetypeInput _ARGS_((Terminal *tPtr, int start));
  212. static int    FormatInput _ARGS_((int command, Fmt_Format format,
  213.                     int inputSize, Address input,
  214.                     int *newInputSizePtr, Address newInput));
  215. static int    FormatOutput _ARGS_((int command, Fmt_Format format,
  216.                      int outputSize, Address output,
  217.                      int *newOutputSizePtr,
  218.                      Address newOutput));
  219.  
  220.  
  221. /*
  222.  *----------------------------------------------------------------------
  223.  *
  224.  * Td_Create --
  225.  *
  226.  *    This procedure creates and initializes a new terminal
  227.  *    driver.
  228.  *
  229.  * Results:
  230.  *    The return value is a handle that is used to refer to the
  231.  *    driver when calling other Td_ procedures, such as Td_Delete.
  232.  *
  233.  * Side effects:
  234.  *    The procedures cookedProc and rawProc may be invoked by
  235.  *    other procedures in this module at later times.  See the
  236.  *    man page for details.
  237.  *
  238.  *----------------------------------------------------------------------
  239.  */
  240.  
  241. Td_Terminal
  242. Td_Create(bufferSize, cookedProc, cookedData, rawProc, rawData)
  243.     int bufferSize;        /* How much buffer space to allow on
  244.                  * output. */
  245.     int (*cookedProc)_ARGS_((ClientData, int operation, int inBufSize, 
  246.                  char *inBuffer, int outBufSize,
  247.                  char *outBuffer)); 
  248.                 /* Procedure to call for control operations
  249.                  * on cooked side of driver. */
  250.     ClientData cookedData;    /* Arbitrary value, provided by caller,
  251.                  * which will be passed to cookedProc
  252.                  * whenever it is invoked. */
  253.     int (*rawProc)_ARGS_((ClientData, int operation, int inBufSize,
  254.               char *inBuffer, int outBufSize,
  255.               char *outBuffer));
  256.                 /* Procedure to call for control operations
  257.                  * on raw side of driver. */
  258.     ClientData rawData;        /* Arbitrary value, provided by caller,
  259.                  * which will be passed to rawProc whenever
  260.                  * it is invoked. */
  261. {
  262.     register Terminal *tPtr;
  263.     Td_BaudRate baud;
  264.  
  265.     tPtr = (Terminal *) malloc(sizeof(Terminal));
  266.     tPtr->sgttyb = sgttybDefault;
  267.     tPtr->tchars = tcharsDefault;
  268.     tPtr->ltchars = ltcharsDefault;
  269.     tPtr->localMode = localModeDefault;
  270.     tPtr->winsize = winsizeDefault;
  271.     tPtr->column = 0;
  272.     tPtr->owner = -1;
  273.     tPtr->flags = 0;
  274.     tPtr->inputBuffer = (char *) malloc(100);
  275.     tPtr->inBufSize = 100;
  276.     tPtr->lastAddedIn = 0;
  277.     tPtr->lastRemovedIn = 0;
  278.     tPtr->lastBreak = 0;
  279.     tPtr->lastHidden = -1;
  280.     tPtr->keyIndex = 0;
  281.     tPtr->keyColumn = 0;
  282.     tPtr->outputBuffer = (char *) malloc(1000);
  283.     tPtr->outBufSize = 1000;
  284.     tPtr->lastAddedOut = 0;
  285.     tPtr->lastRemovedOut = 0;
  286.     tPtr->outCharsBuffered = 0;
  287.     tPtr->cookedOutputLimit = bufferSize;
  288.     tPtr->cookedProc = cookedProc;
  289.     tPtr->cookedData = cookedData;
  290.     tPtr->rawProc = rawProc;
  291.     tPtr->rawData = rawData;
  292.  
  293.     /*
  294.      * Fetch the actual baud rate from the raw device manager.
  295.      */
  296.  
  297.     if ((*rawProc)(rawData, TD_RAW_GET_BAUD_RATE, 0, (char *) NULL,
  298.         sizeof(baud), (char *) &baud) == sizeof(baud)) {
  299.     tPtr->sgttyb.sg_ispeed = baud.ispeed;
  300.     tPtr->sgttyb.sg_ospeed = baud.ospeed;
  301.     }
  302.  
  303.     return (Td_Terminal) tPtr;
  304. }
  305.  
  306. /*
  307.  *----------------------------------------------------------------------
  308.  *
  309.  *  Td_Delete --
  310.  *
  311.  *    Close down a terminal driver, destroying all of the state
  312.  *    associated with it.
  313.  *
  314.  * Results:
  315.  *    None.
  316.  *
  317.  * Side effects:
  318.  *    A hangup is simulated on the cooked side of the driver, and
  319.  *    memory is released.  The caller should never again use
  320.  *    terminal.
  321.  *
  322.  *----------------------------------------------------------------------
  323.  */
  324.  
  325. void
  326. Td_Delete(terminal)
  327.     Td_Terminal terminal;    /* Token identifying terminal (returned
  328.                  * by previous call to Td_Create). */
  329. {
  330.     register Terminal *tPtr = (Terminal *) terminal;
  331.  
  332.     (*tPtr->rawProc)(tPtr->rawData, TD_RAW_SHUTDOWN, 0, (char *) NULL,
  333.         0, (char *) NULL);
  334.     free((char *) tPtr->inputBuffer);
  335.     free((char *) tPtr->outputBuffer);
  336.     free((char *) tPtr);
  337. }
  338.  
  339. /*
  340.  *----------------------------------------------------------------------
  341.  *
  342.  * Td_Open --
  343.  *
  344.  *    This procedure should be called before accepting a new
  345.  *    "open" for a terminal.  It indicates whether new opens
  346.  *    are permitted.
  347.  *
  348.  * Results:
  349.  *    The return value is zero if the open is to be permitted,
  350.  *    and a non-zero errno value if it is to be denied.  If the
  351.  *    open is successful, *selectBitsPtr is filled in with the
  352.  *    initial select state for the terminal.
  353.  *
  354.  * Side effects:
  355.  *    Information counting open streams on the terminal gets
  356.  *    updated.
  357.  *
  358.  *----------------------------------------------------------------------
  359.  */
  360.  
  361. int
  362. Td_Open(terminal, selectBitsPtr)
  363.     Td_Terminal terminal;        /* Token for the terminal to be
  364.                      * checked. */
  365.     int *selectBitsPtr;            /* Put initial select state here. */
  366. {
  367.     register Terminal *tPtr = (Terminal *) terminal;
  368.  
  369.     if (tPtr->flags & EXCLUSIVE) {
  370.     return EBUSY;
  371.     }
  372.     tPtr->numOpens++;
  373.     *selectBitsPtr = 0;
  374.     if ((tPtr->lastBreak != tPtr->lastRemovedIn)
  375.         || (tPtr->localMode & LPENDIN)) {
  376.     *selectBitsPtr |= FS_READABLE;
  377.     }
  378.     if (tPtr->outCharsBuffered < tPtr->cookedOutputLimit) {
  379.     *selectBitsPtr |= FS_WRITABLE;
  380.     }
  381.     return 0;
  382. }
  383.  
  384. /*
  385.  *----------------------------------------------------------------------
  386.  *
  387.  * Td_Close --
  388.  *
  389.  *    This procedure should be called whenever all of the I/O streams
  390.  *    associated with a single open on a terminal have been closed.
  391.  *
  392.  * Results:
  393.  *    none.
  394.  *
  395.  * Side effects:
  396.  *    State in the terminal is updated to reflect the close.
  397.  *
  398.  *----------------------------------------------------------------------
  399.  */
  400.  
  401. void
  402. Td_Close(terminal)
  403.     Td_Terminal terminal;        /* Token for the terminal to be
  404.                      * checked. */
  405. {
  406.     register Terminal *tPtr = (Terminal *) terminal;
  407.  
  408.     tPtr->numOpens--;
  409.     if (tPtr->numOpens == 0) {
  410.     tPtr->flags &= ~EXCLUSIVE;
  411.     }
  412. }
  413.  
  414. /*
  415.  *----------------------------------------------------------------------
  416.  *
  417.  * Td_GetCooked --
  418.  *
  419.  *    Retrieve characters that are ready to be read from the cooked
  420.  *    side of a terminal driver.
  421.  *
  422.  * Results:
  423.  *    The return value is normally 0.  If non-zero, it indicates
  424.  *    that an error occurred and holds a UNIX errno.  Most often,
  425.  *    this is EWOULDBLOCK, meaning that no characters were
  426.  *    available.  The value at numCharsPtr is overwritten with the
  427.  *    actual number of characters returned at buffer, and will be
  428.  *    0 if an error or end-of-file occurred.  *SigNumPtr is overwritten
  429.  *    with a signal to send to the invoking process, or 0.  *SelectBitsPtr
  430.  *    is updated to reflect the readability of the terminal.
  431.  *
  432.  * Side effects:
  433.  *    Characters are removed from the terminal's input buffer.
  434.  *
  435.  *----------------------------------------------------------------------
  436.  */
  437.  
  438. int
  439. Td_GetCooked(terminal, pID, familyID, numCharsPtr, buffer, 
  440.     sigNumPtr, selectBitsPtr)
  441.     Td_Terminal terminal;    /* Token identifying terminal. */
  442.     int pID;            /* Process invoking operation. */
  443.     int familyID;        /* Family of pID. */
  444.     int *numCharsPtr;        /* Points to maximum number of characters to
  445.                  * read from terminal. Overwritten by number
  446.                  * of chars. actually returned. */
  447.     char *buffer;        /* Where to place characters that are read. */
  448.     int *sigNumPtr;        /* Overwrite this with the number of a signal
  449.                  * to generate for the calling process.  0
  450.                  * means no signal. */
  451.     int *selectBitsPtr;        /* The FS_READABLE bit in this word gets
  452.                  * updated to reflect whether or not there
  453.                  * are still more readable characters after
  454.                  * the ones returned. */
  455. {
  456.     register Terminal *tPtr = (Terminal *) terminal;
  457.     register char *dest;
  458.     int count, result;
  459.  
  460.     *sigNumPtr = 0;
  461.  
  462.     /*
  463.      * See if this process owns the terminal.  If not, then signal it
  464.      * and don't give it any input.
  465.      */
  466.  
  467.     if (!(tPtr->flags & OWNER_FAMILY)) {
  468.     if ((pID != tPtr->owner) && (tPtr->owner != -1)) {
  469.         notOwner:
  470.         *sigNumPtr = SIGTTIN;
  471.         *numCharsPtr = 0;
  472.         result = EINTR;
  473.         goto done;
  474.     }
  475.     } else if ((familyID != tPtr->owner) && (tPtr->owner != -1)) {
  476.     goto notOwner;
  477.     }
  478.  
  479.     /*
  480.      * Re-echo buffered characters, if so requested.
  481.      */
  482.  
  483.     if (tPtr->localMode & LPENDIN) {
  484.     int oldCharsBuffered;
  485.  
  486.     oldCharsBuffered = tPtr->outCharsBuffered;
  487.     tPtr->localMode &= ~LPENDIN;
  488.     TdRetypeInput(tPtr, tPtr->lastRemovedIn);
  489.     if ((oldCharsBuffered == 0) && (tPtr->outCharsBuffered != 0) &&
  490.         !(tPtr->flags & OUTPUT_OFF)) {
  491.         (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
  492.             0, (char *) NULL, 0, (char *) NULL);
  493.     }
  494.     }
  495.  
  496.     /*
  497.      * Make sure there's information ready for the terminal.  If not,
  498.      * then block the process.
  499.      */
  500.  
  501.     if (tPtr->lastBreak == tPtr->lastRemovedIn) {
  502.     *numCharsPtr = 0;
  503.     *selectBitsPtr &= ~FS_READABLE;
  504.     return EWOULDBLOCK;
  505.     }
  506.  
  507.     /*
  508.      * Copy bytes from the input buffer to the caller's buffer,
  509.      * and update the terminal's input buffer pointer.
  510.      */
  511.  
  512.     count = 0;
  513.     dest = buffer;
  514.     while ((tPtr->lastRemovedIn != tPtr->lastBreak) &&
  515.         (count < *numCharsPtr)) {
  516.     register char c;
  517.  
  518.     NEXT(tPtr->lastRemovedIn, tPtr->inBufSize, tPtr->lastRemovedIn);
  519.     count++;
  520.     c = tPtr->inputBuffer[tPtr->lastRemovedIn];
  521.     *dest = c;
  522.     dest++;
  523.     if (!(tPtr->sgttyb.sg_flags & (RAW|CBREAK))) {
  524.         if (c == tPtr->tchars.t_eofc) {
  525.         count--;        /* Don't return end-of-file chars. */
  526.         break;
  527.         } else if ((c == '\n') || (c == tPtr->tchars.t_brkc)) {
  528.         break;
  529.         }
  530.     }
  531.     }
  532.     *numCharsPtr = count;
  533.     result = 0;
  534.  
  535.     done:
  536.     if (tPtr->lastBreak == tPtr->lastRemovedIn) {
  537.     *selectBitsPtr &= ~FS_READABLE;
  538.     } else {
  539.     *selectBitsPtr |= FS_READABLE;
  540.     }
  541.     return result;
  542. }
  543.  
  544. /*
  545.  *----------------------------------------------------------------------
  546.  *
  547.  * Td_PutCooked --
  548.  *
  549.  *    Add characters to the output buffer for a terminal.
  550.  *
  551.  * Results:
  552.  *    The return value is always 0 (the output is grown enough to
  553.  *    hold all the output characters).  The value at *numBytesPtr
  554.  *    is left unchanged to indicate that all the characters were
  555.  *    accepted, *sigNumPtr is overwritten with a signal to send
  556.  *    to the invoking process (or 0), and *selectBitsPtr is updated
  557.  *    to reflect whether the terminal's output buffer is now full.
  558.  *
  559.  * Side effects:
  560.  *    Output processing is performed on the characters in buffer,
  561.  *    and they are queued for output on the terminal's raw side.
  562.  *
  563.  *----------------------------------------------------------------------
  564.  */
  565.  
  566.     /* ARGSUSED */
  567. int
  568. Td_PutCooked(terminal, numBytesPtr, buffer, sigNumPtr, selectBitsPtr)
  569.     Td_Terminal terminal;    /* Token identifying terminal. */
  570.     int *numBytesPtr;        /* Points to maximum number of characters
  571.                  * to write to terminal.  Not modified
  572.                  * by this procedure. */
  573.     register char *buffer;    /* Characters to write. */
  574.     int *sigNumPtr;        /* Overwrite this with the number of a signal
  575.                  * to generate for the calling process.  0
  576.                  * means no signal. */
  577.     int *selectBitsPtr;        /* The FS_WRITABLE bit in this word gets
  578.                  * updated to reflect whether or not there
  579.                  * are still more space available in the
  580.                  * terminal's output buffer. */
  581. {
  582.     register Terminal *tPtr = (Terminal *) terminal;
  583.     register char c;
  584.     int i, oldCharsBuffered;
  585.  
  586.     *sigNumPtr = 0;
  587.  
  588.     oldCharsBuffered = tPtr->outCharsBuffered;
  589.     for (i = 0; i < *numBytesPtr; i++, buffer++) {
  590.     c = *buffer;
  591.     if ((tPtr->sgttyb.sg_flags & RAW) || (tPtr->localMode & LLITOUT)) {
  592.         TdPutChar(tPtr, c);
  593.         continue;
  594.     }
  595.     c &= 0177;
  596.     if (c == 04) {    /* End of file (^D) ignored */
  597.         continue;
  598.     } else if ((c == '\n') && (tPtr->sgttyb.sg_flags & CRMOD)) {
  599.         TdPutChar(tPtr, '\r');
  600.         TdPutChar(tPtr, '\n');
  601.     } else {
  602.         TdPutChar(tPtr, c);
  603.     }
  604.     }
  605.     tPtr->keyIndex = tPtr->lastAddedIn;
  606.     tPtr->keyColumn = tPtr->column;
  607.     if (tPtr->lastAddedIn != tPtr->lastRemovedIn) {
  608.     tPtr->lastHidden = tPtr->lastAddedIn;
  609.     }
  610.     if ((oldCharsBuffered == 0) && (tPtr->outCharsBuffered != 0) &&
  611.         !(tPtr->flags & OUTPUT_OFF)) {
  612.     (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
  613.         0, (char *) NULL, 0, (char *) NULL);
  614.     }
  615.     if (tPtr->outCharsBuffered >= tPtr->cookedOutputLimit) {
  616.     *selectBitsPtr &= ~FS_WRITABLE;
  617.     } else {
  618.     *selectBitsPtr |= FS_WRITABLE;
  619.     }
  620.     return 0;
  621. }
  622.  
  623. /*
  624.  *----------------------------------------------------------------------
  625.  *
  626.  * Td_ControlCooked --
  627.  *
  628.  *    This procedure is used to invoke iocontrol operations on the
  629.  *    cooked side of a terminal driver.
  630.  *
  631.  * Results:
  632.  *    If the operation completed successfully then the return value
  633.  *    is zero.  If the operation failed, then the return value is a
  634.  *    UNIX errno indicating what went wrong.  *SigNumPtr gets
  635.  *    overwritten with the number of a signal to send to the invoking
  636.  *    process (or 0), and *selectBitsPtr gets filled in with information
  637.  *    about whether or not the terminal is now readable or writable.
  638.  *
  639.  * Side effects:
  640.  *    Depends on the iocontrol;  see the tty(4) man page for details.
  641.  *
  642.  *----------------------------------------------------------------------
  643.  */
  644.  
  645.     /* ARGSUSED */
  646. int
  647. Td_ControlCooked(terminal, command, format, inputSize, input, outputSizePtr,
  648.     output, sigNumPtr, selectBitsPtr)
  649.     Td_Terminal terminal;        /* Token for terminal. */
  650.     int command;            /* Iocontrol operation to perform
  651.                      * (    e.g. TIOCGETP). */
  652.     Fmt_Format format;            /* Byte-order/alignment format */
  653.     int inputSize;            /* Size of input, in bytes. */
  654.     char *input;            /* Input buffer:  contains information
  655.                      * provided by client as input to
  656.                      * operation. */
  657.     int *outputSizePtr;            /* Largest amount of output that
  658.                      * client is prepared to receive.  This
  659.                      * value is overwritten with the count
  660.                      * of actual bytes returned. */
  661.     char *output;            /* Place to store output bytes;
  662.                      * provided by caller. */
  663.     int *sigNumPtr;            /* Overwrite this with the number of
  664.                      * a signal to generate for the
  665.                      * calling process.  0 means no
  666.                      * signal. */
  667.     int *selectBitsPtr;            /* Store new select state of terminal
  668.                      * here. */
  669. {
  670.     register Terminal *tPtr = (Terminal *) terminal;
  671.     int count, result;
  672.     char *out = (char *) NIL;
  673.     int i;
  674.     Ioc_Owner owner;
  675.     int oldIspeed, oldOspeed, oldFlags, oldStopc, oldStartc;
  676.  
  677.     /*
  678.      * The union below describes all of the possible formats in which
  679.      * the input area may appear.
  680.      */
  681.  
  682.     typedef union {
  683.     int        i;
  684.     char        chars[20];
  685.     struct sgttyb    sgttyb;
  686.     struct tchars    tchars;
  687.     struct ltchars    ltchars;
  688.     Ioc_Owner    owner;
  689.     struct winsize    winsize;
  690.     } InBuf;
  691.     InBuf newInputBuf;
  692.     register InBuf *in = (InBuf *) input;
  693.  
  694.     if (td_Debug) {
  695.     printf("Td_ControlCooked: command %d\n", command);
  696.     }
  697.  
  698.     if (format != FMT_MY_FORMAT) {
  699.     /*
  700.      * Fix up the formatting of the input buffer.
  701.      */
  702.     int newSize = sizeof(newInputBuf);
  703.     if (FormatInput(command, format, inputSize, input,
  704.                 &newSize, (Address) &newInputBuf) != FMT_OK) {
  705.         goto invalid;
  706.     }
  707.     in = &newInputBuf;
  708.     inputSize = newSize;
  709.     }
  710.     /*
  711.      * Save certain pieces of information about the terminal so that
  712.      * if they change we can call the raw control procedure.
  713.      */
  714.  
  715.     oldIspeed = tPtr->sgttyb.sg_ispeed;
  716.     oldOspeed = tPtr->sgttyb.sg_ospeed;
  717.     oldFlags = tPtr->sgttyb.sg_flags;
  718.     oldStopc = tPtr->tchars.t_stopc;
  719.     oldStartc = tPtr->tchars.t_startc;
  720.  
  721.     *sigNumPtr = 0;
  722.     count = 0;
  723.     switch (command) {
  724.  
  725.     case IOC_TTY_SET_DISCIPLINE:
  726.         if ((inputSize != sizeof(int))
  727.             || (in->i != NTTYDISC)) {
  728.         goto invalid;
  729.         }
  730.         break;
  731.  
  732.     case IOC_TTY_GET_DISCIPLINE:
  733.         if (inputSize != 0) {
  734.         goto invalid;
  735.         }
  736.         i = NTTYDISC;
  737.         out = (char *) &i;
  738.         count = sizeof(int);
  739.         break;
  740.  
  741.     case IOC_TTY_GETP:
  742.         if (inputSize != 0) {
  743.         goto invalid;
  744.         }
  745.         out = (char *) &tPtr->sgttyb;
  746.         count = sizeof(struct sgttyb);
  747.         break;
  748.  
  749.     case IOC_TTY_SETP:
  750.         /*
  751.          * Technically, this code should delay until all characters
  752.          * currently buffered for output have been printed, but
  753.          * there's no easy way to do that here:  ioctls must complete
  754.          * immediately.
  755.          */
  756.         TdFlushInput(tPtr);
  757.     case IOC_TTY_SETN:
  758.         if (inputSize != sizeof(struct sgttyb)) {
  759.         goto invalid;
  760.         }
  761.         if ((tPtr->sgttyb.sg_flags ^ in->sgttyb.sg_flags) & RAW) {
  762.         /*
  763.          * Going into or out of raw mode;  always flush input
  764.          * buffer.
  765.          */
  766.  
  767.         TdFlushInput(tPtr);
  768.         }
  769.         tPtr->sgttyb = in->sgttyb;
  770.         break;
  771.  
  772.     case IOC_TTY_EXCL:
  773.         if (inputSize != 0) {
  774.         goto invalid;
  775.         }
  776.         tPtr->flags |= EXCLUSIVE;
  777.         break;
  778.  
  779.     case IOC_TTY_NXCL:
  780.         if (inputSize != 0) {
  781.         goto invalid;
  782.         }
  783.         tPtr->flags &= ~EXCLUSIVE;
  784.         break;
  785.  
  786.     case IOC_TTY_HUP_ON_CLOSE:        /* Not implemented. */
  787.         goto invalid;
  788.  
  789.     case IOC_TTY_FLUSH: {
  790.         /*
  791.          * For compatibility with TIOCFLUSH, we accept one
  792.          * integer argument which has the FREAD and FWRITE bits in it.
  793.          */
  794.         int flags;
  795.         if (inputSize == 0) {
  796.         flags = 0;
  797.         } else {
  798.         flags = in->i;
  799.         }
  800. #ifndef FREAD
  801. #define FREAD    0x1
  802. #define FWRITE    0x2
  803. #endif
  804.         if (flags == 0) {
  805.         flags = FREAD|FWRITE;
  806.         }
  807.         if (flags & FREAD) {
  808.         TdFlushInput(tPtr);
  809.         }
  810.         if (flags & FWRITE) {
  811.         TdFlushOutput(tPtr);
  812.         }
  813.         break;
  814.     }
  815.     case IOC_TTY_INSERT_CHAR:
  816.         if (inputSize != 1) {
  817.         goto invalid;
  818.         }
  819.         Td_PutRaw((Td_Terminal) tPtr, 1, &in->chars[0]);
  820.         break;
  821.  
  822.     case IOC_TTY_SET_BREAK:
  823.         (*tPtr->rawProc)(tPtr->rawData, TD_RAW_START_BREAK,
  824.             0, (char *) NULL, 0, (char *) NULL);
  825.         break;
  826.  
  827.     case IOC_TTY_CLEAR_BREAK:
  828.         (*tPtr->rawProc)(tPtr->rawData, TD_RAW_START_BREAK,
  829.             0, (char *) NULL, 0, (char *) NULL);
  830.         break;
  831.  
  832.     case IOC_TTY_SET_DTR:
  833.         (*tPtr->rawProc)(tPtr->rawData, TD_RAW_SET_DTR,
  834.             0, (char *) NULL, 0, (char *) NULL);
  835.         break;
  836.  
  837.     case IOC_TTY_CLEAR_DTR:    
  838.         (*tPtr->rawProc)(tPtr->rawData, TD_RAW_CLEAR_DTR,
  839.             0, (char *) NULL, 0, (char *) NULL);
  840.         break;
  841.  
  842.     case IOC_GET_OWNER:
  843.         if (inputSize != 0) {
  844.         goto invalid;
  845.         }
  846.         owner.id = tPtr->owner;
  847.         if (tPtr->flags & OWNER_FAMILY) {
  848.         owner.procOrFamily = IOC_OWNER_FAMILY;
  849.         } else {
  850.         owner.procOrFamily = IOC_OWNER_PROC;
  851.         }
  852.         out = (char *) &owner;
  853.         count = sizeof(owner);
  854.         break;
  855.  
  856.     case IOC_SET_OWNER:
  857.         if (inputSize != sizeof(Ioc_Owner)) {
  858.         goto invalid;
  859.         }
  860.         tPtr->owner = in->owner.id;
  861.         if (in->owner.procOrFamily == IOC_OWNER_FAMILY) {
  862.         tPtr->flags |= OWNER_FAMILY;
  863.         } else {
  864.         tPtr->flags &= ~OWNER_FAMILY;
  865.         }
  866.         break;
  867.  
  868.     case IOC_NUM_READABLE:
  869.         i = tPtr->lastBreak - tPtr->lastRemovedIn;
  870.         if (i < 0) {
  871.         i += tPtr->inBufSize;
  872.         }
  873.         out = (char *) &i;
  874.         count = sizeof(int);
  875.         break;
  876.  
  877.     case IOC_TTY_GET_TCHARS:
  878.         if (inputSize != 0) {
  879.         goto invalid;
  880.         }
  881.         out = (char *) &tPtr->tchars;
  882.         count = sizeof(struct tchars);
  883.         break;
  884.  
  885.     case IOC_TTY_SET_TCHARS:
  886.         if (inputSize != sizeof(struct tchars)) {
  887.         goto invalid;
  888.         }
  889.         tPtr->tchars = in->tchars;
  890.         break;
  891.  
  892.     case IOC_TTY_BIS_LM:
  893.         if (inputSize != sizeof(int)) {
  894.         goto invalid;
  895.         }
  896.         tPtr->localMode |= in->i;
  897.         break;
  898.  
  899.     case IOC_TTY_BIC_LM:
  900.         if (inputSize != sizeof(int)) {
  901.         goto invalid;
  902.         }
  903.         tPtr->localMode &= ~in->i;
  904.         break;
  905.  
  906.     case IOC_TTY_SET_LM:
  907.         if (inputSize != sizeof(int)) {
  908.         goto invalid;
  909.         }
  910.         tPtr->localMode = in->i;
  911.         break;
  912.  
  913.     case IOC_TTY_GET_LM:
  914.         if (inputSize != 0) {
  915.         goto invalid;
  916.         }
  917.         out = (char *) &tPtr->localMode;
  918.         count = sizeof(int);
  919.         break;
  920.  
  921.     case IOC_TTY_SET_LTCHARS:
  922.         if (inputSize != sizeof(struct ltchars)) {
  923.         goto invalid;
  924.         }
  925.         tPtr->ltchars = in->ltchars;
  926.         break;
  927.  
  928.     case IOC_TTY_GET_LTCHARS:
  929.         if (inputSize != 0) {
  930.         goto invalid;
  931.         }
  932.         out = (char *) &tPtr->ltchars;
  933.         count = sizeof(struct ltchars);
  934.         break;
  935.  
  936.     case IOC_GET_FLAGS:
  937.         i = 0;
  938.         out = (char *) &i;
  939.         count = sizeof(int);
  940.         break;
  941.     case IOC_SET_FLAGS:
  942.     case IOC_SET_BITS:
  943.     case IOC_CLEAR_BITS:
  944.     case IOC_TTY_NOT_CONTROL_TTY:
  945.         break;
  946.  
  947.     case IOC_TTY_GET_WINDOW_SIZE:
  948.         if (inputSize != 0) {
  949.         goto invalid;
  950.         }
  951.         out = (char *) &tPtr->winsize;
  952.         count = sizeof(struct winsize);
  953.         break;
  954.  
  955.     case IOC_TTY_SET_WINDOW_SIZE: {
  956.         Td_Signal signalInfo;
  957.         if (inputSize != sizeof(struct winsize)) {
  958.         goto invalid;
  959.         }
  960.         tPtr->winsize = in->winsize;
  961.         signalInfo.sigNum = SIGWINCH;
  962.         signalInfo.groupID = tPtr->owner;
  963.         (*tPtr->cookedProc)(tPtr->cookedData, TD_COOKED_SIGNAL,
  964.             sizeof(signalInfo), (char *) &signalInfo,
  965.             0, (char *) NULL);
  966.         break;
  967.     }
  968.  
  969.     default:
  970.         goto invalid;
  971.     }
  972.     /*
  973.      * Fix up output buffer for the client.  At this point
  974.      * count = size of output data
  975.      * out = pointer to output data
  976.      * Here we rely on the Fmt_ library doing essentially a bcopy
  977.      * if our format and the client's format are the same.
  978.      */
  979.     if (count != 0) {
  980.     result = FormatOutput(command, format, count, out,
  981.                   outputSizePtr, output);
  982.     if (result != FMT_OK) {
  983.         result = EINVAL;
  984.         if (td_Debug) {
  985.         printf("Td_ControlCooked: command %d invalid output\n", command);
  986.         }
  987.     }
  988.     } else {
  989.     *outputSizePtr = 0;
  990.     result = 0;
  991.     }
  992.  
  993.     /*
  994.      * Call the raw control procedure if anything changed that it needs
  995.      * to know about.
  996.      */
  997.  
  998.     if ((oldIspeed != tPtr->sgttyb.sg_ispeed)
  999.         || (oldOspeed != tPtr->sgttyb.sg_ospeed)) {
  1000.     Td_BaudRate baud, baud2;
  1001.  
  1002.     baud.ispeed = tPtr->sgttyb.sg_ispeed;
  1003.     baud.ospeed = tPtr->sgttyb.sg_ospeed;
  1004.     if ((*tPtr->rawProc)(tPtr->rawData, TD_RAW_SET_BAUD_RATE,
  1005.         sizeof(baud), (char *) &baud,
  1006.         sizeof(baud2), (char *) &baud2) == sizeof(baud2)) {
  1007.  
  1008.         /*
  1009.          * Device has overridden the baud-rate change;  take its advice.
  1010.          */
  1011.  
  1012.         tPtr->sgttyb.sg_ispeed = baud2.ispeed;
  1013.         tPtr->sgttyb.sg_ospeed = baud2.ospeed;
  1014.     }
  1015.     }
  1016.     if (((oldFlags & RAW) != (tPtr->sgttyb.sg_flags & RAW))
  1017.         || (oldStopc != tPtr->tchars.t_stopc)
  1018.         || (oldStartc != tPtr->tchars.t_startc)) {
  1019.     Td_FlowChars flow;
  1020.  
  1021.     if (tPtr->sgttyb.sg_flags & RAW) {
  1022.         flow.stop = flow.start = -1;
  1023.     } else {
  1024.         flow.stop = tPtr->tchars.t_stopc;
  1025.         flow.start = tPtr->tchars.t_startc;
  1026.     }
  1027.     (*tPtr->rawProc)(tPtr->rawData, TD_RAW_FLOW_CHARS,
  1028.         sizeof(flow), (char *) &flow, 0, (char *) NULL);
  1029.     }
  1030.  
  1031.     /*
  1032.      * Setting the select bits is a bit tricky:  if LPENDIN is set, then
  1033.      * we need to find out when the next read is done.  So, make the device
  1034.      * appear to be readable even if it isn't.  Otherwise, we won't be told
  1035.      * when the device is read.
  1036.      */
  1037.  
  1038.     done:
  1039.     *selectBitsPtr = 0;
  1040.     if ((tPtr->lastBreak != tPtr->lastRemovedIn)
  1041.         || (tPtr->localMode & LPENDIN)) {
  1042.     *selectBitsPtr |= FS_READABLE;
  1043.     }
  1044.     if (tPtr->outCharsBuffered < tPtr->cookedOutputLimit) {
  1045.     *selectBitsPtr |= FS_WRITABLE;
  1046.     }
  1047.     return result;
  1048.  
  1049.     invalid:
  1050.     if (td_Debug) {
  1051.     printf("Td_ControlCooked: command %d invalid input\n", command);
  1052.     }
  1053.     *outputSizePtr = 0;
  1054.     result = EINVAL;
  1055.     goto done;
  1056. }
  1057.  
  1058. /*
  1059.  *----------------------------------------------------------------------
  1060.  *
  1061.  * FormatInput --
  1062.  *
  1063.  *    Re-format the input buffer of an I/O control.  This is required
  1064.  *    if the client is on a host with a different byte order/alignment.
  1065.  *    This uses the Fmt_Convert library routine.
  1066.  *
  1067.  * Results:
  1068.  *    This returns zero if all goes well.
  1069.  *    Otherwise a FMT_ error code is returned.
  1070.  *
  1071.  * Side effects:
  1072.  *    The reformatted input is put into newBuffer.  The true size of
  1073.  *    the data in this buffer is returned in *newInputSizePtr.
  1074.  *
  1075.  *----------------------------------------------------------------------
  1076.  */
  1077.  
  1078. static int
  1079. FormatInput(command, format, inputSize, input, newInputSizePtr, newInput)
  1080.     int command;        /* I/O Control command */
  1081.     Fmt_Format format;        /* Format of client host */
  1082.     int inputSize;        /* Size of input buffer */
  1083.     Address input;        /* Input buffer in client format */
  1084.     int *newInputSizePtr;    /* In/Out - Size of new input buffer */
  1085.     Address newInput;        /* Out - Input buffer in our format */
  1086. {
  1087.     int status = FMT_OK;
  1088.     char *fmtString = "";
  1089.  
  1090.     switch (command) {
  1091.  
  1092.     case IOC_TTY_GET_DISCIPLINE:
  1093.     case IOC_TTY_GETP:
  1094.     case IOC_TTY_EXCL:
  1095.     case IOC_TTY_NXCL:
  1096.     case IOC_GET_OWNER:
  1097.     case IOC_TTY_GET_TCHARS:
  1098.     case IOC_TTY_GET_LM:
  1099.     case IOC_TTY_GET_LTCHARS:
  1100.     case IOC_TTY_GET_WINDOW_SIZE:
  1101.     case IOC_TTY_NOT_CONTROL_TTY:
  1102.     default:
  1103.         *newInputSizePtr = 0;
  1104.         goto noconversion;
  1105.  
  1106.     case IOC_TTY_FLUSH:
  1107.         /*
  1108.          * Optional int
  1109.          */
  1110.         if (inputSize == 0) {
  1111.         *newInputSizePtr = 0;
  1112.         goto noconversion;
  1113.         } else {
  1114.         fmtString = "w";
  1115.         }
  1116.         break;
  1117.  
  1118.     case IOC_TTY_SET_DISCIPLINE:
  1119.     case IOC_TTY_BIS_LM:
  1120.     case IOC_TTY_BIC_LM:
  1121.     case IOC_TTY_SET_LM:
  1122.         /*
  1123.          * One int
  1124.          */
  1125.         fmtString = "w";
  1126.         break;
  1127.  
  1128.     case IOC_TTY_SETP:
  1129.     case IOC_TTY_SETN:
  1130.         /*
  1131.          * struct sgttyb
  1132.          */
  1133.         fmtString = "{b4h}";
  1134.         break;
  1135.  
  1136.     case IOC_TTY_INSERT_CHAR:
  1137.         /*
  1138.          * One char
  1139.          */
  1140.         fmtString = "b";
  1141.         break;
  1142.  
  1143.     case IOC_SET_OWNER:
  1144.         /*
  1145.          * Ioc_Owner
  1146.          */
  1147.         fmtString = "{w2}";
  1148.         break;
  1149.  
  1150.     case IOC_TTY_SET_TCHARS:
  1151.         /*
  1152.          * struct tchars
  1153.          */
  1154.         fmtString = "{b6}";
  1155.         break;
  1156.  
  1157.     case IOC_TTY_SET_LTCHARS:
  1158.         /*
  1159.          * struct ltchars
  1160.          */
  1161.         fmtString = "{b6}";
  1162.         break;
  1163.  
  1164.  
  1165.     case IOC_TTY_SET_WINDOW_SIZE: {
  1166.         /*
  1167.          * struct winsize
  1168.          */
  1169.         fmtString = "{h4}";
  1170.         break;
  1171.     }
  1172.  
  1173.     }
  1174.     status = Fmt_Convert(fmtString, format, &inputSize, input, FMT_MY_FORMAT,
  1175.         newInputSizePtr, newInput);
  1176. noconversion:
  1177.     return(status);
  1178. }
  1179.  
  1180. /*
  1181.  *----------------------------------------------------------------------
  1182.  *
  1183.  * FormatOutput --
  1184.  *
  1185.  *    Re-format the output buffer of an I/O control.
  1186.  *    This uses the Fmt_Convert library routine.
  1187.  *
  1188.  * Results:
  1189.  *    This returns zero if all goes well.
  1190.  *    Otherwise a FMT_ error code is returned.
  1191.  *
  1192.  * Side effects:
  1193.  *    The reformatted output is put into newOutput.  The true size of
  1194.  *    the data in this buffer is returned in *newOutputSizePtr.
  1195.  *
  1196.  *----------------------------------------------------------------------
  1197.  */
  1198.  
  1199. static int
  1200. FormatOutput(command, format, outputSize, output, newOutputSizePtr, newOutput)
  1201.     int command;        /* I/O Control command */
  1202.     Fmt_Format format;        /* Format of client host */
  1203.     int outputSize;        /* Size of input buffer (in our format) */
  1204.     Address output;        /* Output buffer in our format */
  1205.     int *newOutputSizePtr;    /* In/Out - Size of new output buffer */
  1206.     Address newOutput;        /* Out - Output buffer in the client's format */
  1207. {
  1208.     int status = FMT_OK;
  1209.     char *fmtString = "";
  1210.  
  1211.     switch (command) {
  1212.  
  1213.     case IOC_TTY_SET_DISCIPLINE:
  1214.     case IOC_TTY_SETP:
  1215.     case IOC_TTY_SETN:
  1216.     case IOC_TTY_EXCL:
  1217.     case IOC_TTY_NXCL:
  1218.     case IOC_TTY_FLUSH:
  1219.     case IOC_TTY_INSERT_CHAR:
  1220.     case IOC_SET_OWNER:
  1221.     case IOC_TTY_SET_TCHARS:
  1222.     case IOC_TTY_BIS_LM:
  1223.     case IOC_TTY_BIC_LM:
  1224.     case IOC_TTY_SET_LM:
  1225.     case IOC_TTY_SET_LTCHARS:
  1226.     case IOC_TTY_NOT_CONTROL_TTY:
  1227.     default:
  1228.         *newOutputSizePtr = 0;
  1229.         goto noconversion;
  1230.  
  1231.     case IOC_TTY_GET_DISCIPLINE:
  1232.     case IOC_NUM_READABLE:
  1233.     case IOC_TTY_GET_LM:
  1234.     case IOC_GET_FLAGS:
  1235.         /*
  1236.          * One int
  1237.          */
  1238.         fmtString = "w";
  1239.         break;
  1240.  
  1241.     case IOC_TTY_GETP:
  1242.         /*
  1243.          * struct sgttyb
  1244.          */
  1245.         fmtString = "{b4h}";
  1246.         break;
  1247.  
  1248.     case IOC_GET_OWNER:
  1249.         /*
  1250.          * Ioc_Owner
  1251.          */
  1252.         fmtString = "{w2}";
  1253.         break;
  1254.  
  1255.     case IOC_TTY_GET_TCHARS:
  1256.         /*
  1257.          * struct tchars
  1258.          */
  1259.         fmtString = "{b6}";
  1260.         break;
  1261.  
  1262.     case IOC_TTY_GET_LTCHARS:
  1263.         /*
  1264.          * struct ltchars
  1265.          */
  1266.         fmtString = "{b6}";
  1267.         break;
  1268.  
  1269.     case IOC_TTY_GET_WINDOW_SIZE:
  1270.         /*
  1271.          * struct winsize
  1272.          */
  1273.         fmtString = "{h4}";
  1274.         break;
  1275.     }
  1276.     status = Fmt_Convert(fmtString, FMT_MY_FORMAT, &outputSize, output, format,
  1277.         newOutputSizePtr, newOutput);
  1278. noconversion:
  1279.     return(status);
  1280. }
  1281.  
  1282. /*
  1283.  *----------------------------------------------------------------------
  1284.  *
  1285.  * Td_GetRaw --
  1286.  *
  1287.  *    Retrieve characters that are ready to be output from the
  1288.  *    terminal driver to the raw device.
  1289.  *
  1290.  * Results:
  1291.  *    The return value is a count of the number of characters
  1292.  *    actually returned at buffer.  This will be less than or
  1293.  *    equal to numChars.  A return value of 0 indicates that
  1294.  *    there are no characters in terminal's output buffer or
  1295.  *    that output has been disabled.
  1296.  *
  1297.  * Side effects:
  1298.  *    Characters are removed from the terminal's output buffer,
  1299.  *    and the cooked side may be notified that the terminal is
  1300.  *    writable again.
  1301.  *
  1302.  *----------------------------------------------------------------------
  1303.  */
  1304.  
  1305. int
  1306. Td_GetRaw(terminal, numChars, buffer)
  1307.     Td_Terminal terminal;    /* Token identifying terminal. */
  1308.     int numChars;        /* Maximum number of characters to read
  1309.                  * from terminal's output buffer. */
  1310.     register char *buffer;    /* Where to place characters that are read. */
  1311. {
  1312.     register Terminal *tPtr = (Terminal *) terminal;
  1313.     int count;
  1314.  
  1315.     if (tPtr->flags & OUTPUT_OFF) {
  1316.     return 0;
  1317.     }
  1318.     for (count = 0; count < numChars; count++, buffer++) {
  1319.     if (tPtr->lastRemovedOut == tPtr->lastAddedOut) {
  1320.         break;
  1321.     }
  1322.     NEXT(tPtr->lastRemovedOut, tPtr->outBufSize, tPtr->lastRemovedOut);
  1323.     *buffer = tPtr->outputBuffer[tPtr->lastRemovedOut];
  1324.     }
  1325.     tPtr->outCharsBuffered -= count;
  1326.     if (tPtr->outCharsBuffered < tPtr->cookedOutputLimit) {
  1327.     (*tPtr->cookedProc)(tPtr->cookedData, TD_COOKED_WRITES_OK,
  1328.         0, (char *) NULL, 0, (char *) NULL);
  1329.     }
  1330.     return count;
  1331. }
  1332.  
  1333. /*
  1334.  *----------------------------------------------------------------------
  1335.  *
  1336.  * Td_PutRaw --
  1337.  *
  1338.  *    This procedure is invoked when characters arrive from the
  1339.  *    raw device associated with the terminal (e.g., from the
  1340.  *    keyboard).  It adds them to the input buffer of the terminal
  1341.  *    and does appropriate line editing etc.
  1342.  *
  1343.  * Results:
  1344.  *    None.
  1345.  *
  1346.  * Side effects:
  1347.  *    Characters are added to the input buffer, and may be made
  1348.  *    available on the cooked side of the terminal.  Echoed
  1349.  *    characters get added to the output buffer, which could result
  1350.  *    in a call to the raw control procedure.
  1351.  *
  1352.  *----------------------------------------------------------------------
  1353.  */
  1354.  
  1355. void
  1356. Td_PutRaw(terminal, numChars, buffer)
  1357.     Td_Terminal terminal;    /* Token identifying terminal. */
  1358.     int numChars;        /* Number of characters to process. */
  1359.     char *buffer;        /* Characters that were ostensibly typed
  1360.                  * on the raw device's keyboard. */
  1361. {
  1362.     register Terminal *tPtr = (Terminal *) terminal;
  1363.     int next, oldCharsBuffered;
  1364.     register char c = '\0';    /* dummy initial value */
  1365.     Td_Signal signalInfo;
  1366.  
  1367.     oldCharsBuffered = tPtr->outCharsBuffered;
  1368.     tPtr->localMode &= ~LFLUSHO;
  1369.  
  1370.     /*
  1371.      * According to the 4.3 BSD manual page, we should re-echo everything
  1372.      * in the input buffer if LPENDIN is set here.  But this appears to
  1373.      * produce the wrong results and I suspect that it isn't even
  1374.      * implemented in BSD.  So it's not implemented here either.
  1375.      */
  1376.  
  1377.     for ( ; numChars > 0; numChars--, buffer++) {
  1378.     c = *buffer;
  1379.  
  1380.     /*
  1381.      * Skip all further processing if in raw mode.
  1382.      */
  1383.     
  1384.     if (tPtr->sgttyb.sg_flags & RAW) {
  1385.         goto addToBuffer;
  1386.     }
  1387.     c &= 0x7f;
  1388.  
  1389.     /*
  1390.      * Handle flow-control characters.
  1391.      */
  1392.  
  1393.     
  1394.     if (c == tPtr->tchars.t_stopc) {
  1395.         if (tPtr->flags & OUTPUT_OFF) {
  1396.         if (c == tPtr->tchars.t_startc) {
  1397.             goto restartOutput;
  1398.         }
  1399.         } else {
  1400.         tPtr->flags |= OUTPUT_OFF;
  1401.         }
  1402.         continue;
  1403.     } else if (c == tPtr->tchars.t_startc) {
  1404.         restartOutput:
  1405.         tPtr->flags &= ~OUTPUT_OFF;
  1406.         if (tPtr->outCharsBuffered != 0) {
  1407.         (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
  1408.             0, (char *) NULL, 0, (char *) NULL);
  1409.         }
  1410.         continue;
  1411.     } else if ((tPtr->flags & OUTPUT_OFF) && !(tPtr->localMode & LDECCTQ)) {
  1412.         tPtr->flags &= ~OUTPUT_OFF;
  1413.         if (tPtr->outCharsBuffered != 0) {
  1414.         (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
  1415.             0, (char *) NULL, 0, (char *) NULL);
  1416.         }
  1417.     }
  1418.  
  1419.     /*
  1420.      * If the last character typed was a "quote" character, then
  1421.      * just add the new character to the input buffer without
  1422.      * additional processing.
  1423.      */
  1424.  
  1425.     if (tPtr->flags & LITERAL_NEXT) {
  1426.         tPtr->flags &= ~LITERAL_NEXT;
  1427.         goto addToBuffer;
  1428.     }
  1429.  
  1430.     /*
  1431.      * Handle output-flushing character.  Clearing oldCharsBuffered
  1432.      * is essential, otherwise, the raw client won't be notified if
  1433.      * characters are added to the output buffer in this procedure.
  1434.      */
  1435.  
  1436.     if (c == tPtr->ltchars.t_flushc) {
  1437.         if (tPtr->localMode & LFLUSHO) {
  1438.         tPtr->localMode &= ~LFLUSHO;
  1439.         } else {
  1440.         TdFlushOutput(tPtr);
  1441.         oldCharsBuffered = 0;
  1442.         TdEcho(tPtr, c);
  1443.         TdRetypeInput(tPtr, tPtr->lastRemovedIn);
  1444.         tPtr->localMode |= LFLUSHO;
  1445.         }
  1446.         continue;
  1447.     }
  1448.     
  1449.     /*
  1450.      * Handle line-editing characters such as erase and kill.
  1451.      */
  1452.     
  1453.     if ((c == '\r') && (tPtr->sgttyb.sg_flags & CRMOD)) {
  1454.         c = '\n';
  1455.     }
  1456.     if (!(tPtr->sgttyb.sg_flags & CBREAK)) {
  1457.         if (c == tPtr->sgttyb.sg_erase) {        /* Backspace. */
  1458.         if (tPtr->lastAddedIn != tPtr->lastBreak) {
  1459.             TdBackspace(tPtr);
  1460.         }
  1461.         continue;
  1462.         } else if (c == tPtr->ltchars.t_werasc) {    /* Delete word. */
  1463.         int gotNonSpace = 0;
  1464.     
  1465.         while (tPtr->lastAddedIn != tPtr->lastBreak) {
  1466.             if (isspace(tPtr->inputBuffer[tPtr->lastAddedIn])) {
  1467.             if (gotNonSpace) {
  1468.                 break;
  1469.             }
  1470.             } else {
  1471.             gotNonSpace = 1;
  1472.             }
  1473.             TdBackspace(tPtr);
  1474.         }
  1475.         continue;
  1476.         } else if (c == tPtr->sgttyb.sg_kill) {    /* Delete line. */
  1477.         if ((tPtr->lastHidden != -1) || !(tPtr->localMode & LCRTKIL)) {
  1478.             TdEcho(tPtr, c);
  1479.             TdEcho(tPtr, '\n');
  1480.             tPtr->lastAddedIn = tPtr->lastBreak;
  1481.             tPtr->lastHidden = -1;
  1482.         } else {
  1483.             while (tPtr->lastAddedIn != tPtr->lastBreak) {
  1484.             TdBackspace(tPtr);
  1485.             }
  1486.         }
  1487.         continue;
  1488.         } else if (c == tPtr->ltchars.t_rprntc) {    /* Re-echo all. */
  1489.         TdEcho(tPtr, c);
  1490.         TdEcho(tPtr, '\n');
  1491.         TdRetypeInput(tPtr, tPtr->lastRemovedIn);
  1492.         continue;
  1493.         }
  1494.     }
  1495.  
  1496.     if (c == tPtr->ltchars.t_lnextc) {
  1497.         tPtr->flags |= LITERAL_NEXT;
  1498.         continue;
  1499.     }
  1500.     
  1501.     /*
  1502.      * Generate signals in response to certain input characters.  If this
  1503.      * isn't a signal character, then officially add it to the input
  1504.      * buffer.
  1505.      */
  1506.     
  1507.     if (c == tPtr->tchars.t_intrc) {
  1508.         signalInfo.sigNum = SIGINT;
  1509.         sendSignal:
  1510.         signalInfo.groupID = tPtr->owner;
  1511.         (*tPtr->cookedProc)(tPtr->cookedData, TD_COOKED_SIGNAL,
  1512.             sizeof(signalInfo), (char *) &signalInfo,
  1513.             0, (char *) NULL);
  1514.         TdFlushInput(tPtr);
  1515.         TdFlushOutput(tPtr);
  1516.         oldCharsBuffered = 0;
  1517.         goto echo;
  1518.     } else if (c == tPtr->tchars.t_quitc) {
  1519.         signalInfo.sigNum = SIGQUIT;
  1520.         goto sendSignal;
  1521.     } else if (c == tPtr->ltchars.t_suspc) {
  1522.         signalInfo.sigNum = SIGTSTP;
  1523.         goto sendSignal;
  1524.     }
  1525.     
  1526.     /*
  1527.      * If the buffer is full, then reallocate it with a size twice as
  1528.      * large.  Then add the character to the buffer.
  1529.      */
  1530.     
  1531.     addToBuffer:
  1532.     NEXT(tPtr->lastAddedIn, tPtr->inBufSize, next);
  1533.     if (next == tPtr->lastRemovedIn) {
  1534.         char *newBuffer;
  1535.         int src, dst;
  1536.     
  1537.         newBuffer = malloc((unsigned) 2*tPtr->inBufSize);
  1538.         for (src = tPtr->lastRemovedIn, dst = 0; src != tPtr->lastAddedIn; ) {
  1539.         NEXT(src, tPtr->inBufSize, src);
  1540.         dst += 1;
  1541.         newBuffer[dst] = tPtr->inputBuffer[src];
  1542.         }
  1543.         tPtr->lastBreak -= tPtr->lastRemovedIn;
  1544.         if (tPtr->lastBreak < 0) {
  1545.         tPtr->lastBreak += tPtr->inBufSize;
  1546.         }
  1547.         if (tPtr->lastHidden != -1) {
  1548.         tPtr->lastHidden -= tPtr->lastRemovedIn;
  1549.         if (tPtr->lastHidden < 0) {
  1550.             tPtr->lastHidden += tPtr->inBufSize;
  1551.         }
  1552.         }
  1553.         tPtr->inputBuffer = newBuffer;
  1554.         tPtr->inBufSize *= 2;
  1555.         tPtr->lastRemovedIn = 0;
  1556.         tPtr->lastAddedIn = dst;
  1557.         NEXT(dst, tPtr->inBufSize, next);
  1558.     }
  1559.     tPtr->inputBuffer[next] = c;
  1560.     tPtr->lastAddedIn = next;
  1561.     
  1562.     /*
  1563.      * Echo.
  1564.      */
  1565.     
  1566.     echo:
  1567.     if ((tPtr->sgttyb.sg_flags & ECHO) && !(tPtr->sgttyb.sg_flags & RAW)) {
  1568.         if (tPtr->flags & BS_IN_PROGRESS) {
  1569.         TdPutChar(tPtr, '/');
  1570.         tPtr->flags &= ~BS_IN_PROGRESS;
  1571.         }
  1572.         TdEcho(tPtr, c);
  1573.     }
  1574.     }
  1575.  
  1576.     /*
  1577.      * Are there any characters that are ready for reading?  If so,
  1578.      * change the terminal's state to be readable and notify the
  1579.      * cooked side.
  1580.      */
  1581.  
  1582.     if ((tPtr->sgttyb.sg_flags & (RAW|CBREAK)) || (c == tPtr->tchars.t_eofc) ||
  1583.         (c == tPtr->tchars.t_brkc) || (c == '\n')) {
  1584.     tPtr->lastBreak = tPtr->lastAddedIn;
  1585.     tPtr->lastHidden = -1;
  1586.     tPtr->keyIndex = tPtr->lastAddedIn;
  1587.     tPtr->keyColumn = tPtr->column;
  1588.     (*tPtr->cookedProc)(tPtr->cookedData, TD_COOKED_READS_OK,
  1589.         0, (char *) NULL, 0, (char *) NULL);
  1590.     }
  1591.  
  1592.     /*
  1593.      * If the output buffer just became non-empty, then notify the
  1594.      * raw control procedure.
  1595.      */
  1596.  
  1597.     if ((oldCharsBuffered == 0) && (tPtr->outCharsBuffered != 0) &&
  1598.         !(tPtr->flags & OUTPUT_OFF)) {
  1599.     (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
  1600.         0, (char *) NULL, 0, (char *) NULL);
  1601.     }
  1602. }
  1603.  
  1604. /*
  1605.  *----------------------------------------------------------------------
  1606.  *
  1607.  * Td_ControlRaw --
  1608.  *
  1609.  *    This procedure is used to tell the terminal driver that
  1610.  *    certain special things happened on the raw side of the
  1611.  *    terminal, such as a hangup or break.
  1612.  *
  1613.  * Results:
  1614.  *    None.
  1615.  *
  1616.  * Side effects:
  1617.  *    Depends on the operation;  see the man page for details
  1618.  *    on what commands may be invoked.
  1619.  *
  1620.  *----------------------------------------------------------------------
  1621.  */
  1622.  
  1623.     /* ARGSUSED */
  1624. void
  1625. Td_ControlRaw(terminal, operation)
  1626.     Td_Terminal terminal;        /* Token for terminal. */
  1627.     int operation;            /* What just happened: TD_BREAK etc. */
  1628. {
  1629.     register Terminal *tPtr = (Terminal *) terminal;
  1630.  
  1631.     switch (operation) {
  1632.     case TD_BREAK: {
  1633.  
  1634.         /*
  1635.          * Reset some of the terminal state, such as flow control,
  1636.          * then pretend an interrupt character was typed.
  1637.          */
  1638.  
  1639.         tPtr->flags &= ~(OUTPUT_OFF | BS_IN_PROGRESS | LITERAL_NEXT);
  1640.         if (tPtr->sgttyb.sg_flags & RAW) {
  1641.         char c = 0;
  1642.         Td_PutRaw(terminal, 1, &c);
  1643.         } else if ((int) tPtr->tchars.t_intrc != -1) {
  1644.         Td_PutRaw(terminal, 1, &tPtr->tchars.t_intrc);
  1645.         }
  1646.         if (tPtr->outCharsBuffered != 0) {
  1647.         (*tPtr->rawProc)(tPtr->rawData, TD_RAW_OUTPUT_READY,
  1648.             0, (char *) NULL, 0, (char *) NULL);
  1649.         }
  1650.         break;
  1651.     }
  1652.     }
  1653. }
  1654.  
  1655. /*
  1656.  *----------------------------------------------------------------------
  1657.  *
  1658.  * TdPutChar --
  1659.  *
  1660.  *    Add a character to the output buffer associated with a
  1661.  *    terminal, and keep track of the current column while outputting
  1662.  *    the character.  This routine also substitutes spaces for tabs,
  1663.  *    if that's the mode the terminal is in.
  1664.  *
  1665.  * Results:
  1666.  *    None.
  1667.  *
  1668.  * Side effects:
  1669.  *    TPtr->column gets updated and stuff gets added to the terminal's
  1670.  *    output buffer.  The output buffer will get grown if necessary.
  1671.  *
  1672.  *----------------------------------------------------------------------
  1673.  */
  1674.  
  1675. static void
  1676. TdPutChar(tPtr, c)
  1677.     register Terminal *tPtr;    /* Terminal on which to output. */
  1678.     char c;            /* Character to output. */
  1679. {
  1680.     /*
  1681.      * Ignore the character if output is being flushed.
  1682.      */
  1683.  
  1684.     if (tPtr->localMode & LFLUSHO) {
  1685.     return;
  1686.     }
  1687.  
  1688.     /*
  1689.      * Grow the output buffer if there isn't enough space for the
  1690.      * largest amount of information this procedure might want to
  1691.      * add to it.
  1692.      */
  1693.  
  1694.     if ((tPtr->outCharsBuffered + 8) >= tPtr->outBufSize) {
  1695.     char *newBuffer;
  1696.     int dst;
  1697.  
  1698.     newBuffer = malloc((unsigned) (2*tPtr->outBufSize));
  1699.     for (dst = 0; tPtr->lastRemovedOut != tPtr->lastAddedOut; ) {
  1700.         dst += 1;
  1701.         NEXT(tPtr->lastRemovedOut, tPtr->outBufSize, tPtr->lastRemovedOut);
  1702.         newBuffer[dst] = tPtr->outputBuffer[tPtr->lastRemovedOut];
  1703.     }
  1704.     tPtr->outputBuffer = newBuffer;
  1705.     tPtr->outBufSize *= 2;
  1706.     tPtr->lastRemovedOut = 0;
  1707.     tPtr->lastAddedOut = dst;
  1708.     }
  1709.  
  1710.     /*
  1711.      * Update column position, and add character(s) to the buffer.
  1712.      */
  1713.  
  1714.     if (isprint(c)) {
  1715.     tPtr->column += 1;
  1716.     } else if (c == '\r') {
  1717.     tPtr->column = 0;
  1718.     } else if (c == '\t') {
  1719.     int count = 8 - (tPtr->column & 07);
  1720.  
  1721.     tPtr->column += count;
  1722.     if ((tPtr->sgttyb.sg_flags & TBDELAY) == XTABS) {
  1723.         for ( ; count > 0; count--) {
  1724.         NEXT(tPtr->lastAddedOut, tPtr->outBufSize, tPtr->lastAddedOut);
  1725.         tPtr->outputBuffer[tPtr->lastAddedOut] = ' ';
  1726.         tPtr->outCharsBuffered++;
  1727.         }
  1728.         return;
  1729.     }
  1730.     } else if (c == '\b') {
  1731.     tPtr->column -= 1;
  1732.     }
  1733.     NEXT(tPtr->lastAddedOut, tPtr->outBufSize, tPtr->lastAddedOut);
  1734.     tPtr->outputBuffer[tPtr->lastAddedOut] = c;
  1735.     tPtr->outCharsBuffered++;
  1736. }
  1737.  
  1738. /*
  1739.  *----------------------------------------------------------------------
  1740.  *
  1741.  * TdEcho --
  1742.  *
  1743.  *    Echo a character on a terminal, if echoing is enabled.
  1744.  *
  1745.  * Results:
  1746.  *    None.
  1747.  *
  1748.  * Side effects:
  1749.  *    The appropriate echo sequence for c gets added to the 
  1750.  *    terminal's output buffer.
  1751.  *
  1752.  *----------------------------------------------------------------------
  1753.  */
  1754.  
  1755. static void
  1756. TdEcho(tPtr, c)
  1757.     register Terminal *tPtr;    /* Terminal for which to echo. */
  1758.     register char c;        /* Character to echo. */
  1759. {
  1760.     if (!(tPtr->sgttyb.sg_flags & ECHO)) {
  1761.     return;
  1762.     }
  1763.     if (isprint(c)) {
  1764.     TdPutChar(tPtr, c);
  1765.     } else if (c == '\n') {
  1766.     if (tPtr->sgttyb.sg_flags & CRMOD) {
  1767.         TdPutChar(tPtr, '\r');
  1768.     }
  1769.     TdPutChar(tPtr, '\n');
  1770.     } else if (c == '\t') {
  1771.     TdPutChar(tPtr, c);
  1772.     } else if (c == 04) {
  1773.     /* Don't echo control-D's. */
  1774.     } else if (tPtr->localMode & LCTLECH) {
  1775.     TdPutChar(tPtr, '^');
  1776.     if (c == 0177) {
  1777.         TdPutChar(tPtr, '?');
  1778.     } else {
  1779.         TdPutChar(tPtr, c + 'A' - 1);
  1780.     }
  1781.     } else {
  1782.     TdPutChar(tPtr, c);
  1783.     }
  1784. }
  1785.  
  1786. /*
  1787.  *----------------------------------------------------------------------
  1788.  *
  1789.  * TdRetypeInput --
  1790.  *
  1791.  *    This procedure is called to re-echo all of the characters in
  1792.  *    the input buffer.
  1793.  *
  1794.  * Results:
  1795.  *    None.
  1796.  *
  1797.  * Side effects:
  1798.  *    Characters get added to the terminal's output buffer.
  1799.  *
  1800.  *----------------------------------------------------------------------
  1801.  */
  1802.  
  1803. static void
  1804. TdRetypeInput(tPtr, start)
  1805.     register Terminal *tPtr;    /* Which terminal to re-echo for. */
  1806.     int start;            /* Index within tPtr's buffer:  start
  1807.                  * echoing at the character AFTER this one. */
  1808. {
  1809.     tPtr->keyIndex = start;
  1810.     tPtr->keyColumn = tPtr->column;
  1811.     while (start != tPtr->lastAddedIn) {
  1812.     NEXT(start, tPtr->inBufSize, start);
  1813.     TdEcho(tPtr, tPtr->inputBuffer[start]);
  1814.     }
  1815.     tPtr->lastHidden = -1;
  1816. }
  1817.  
  1818. /*
  1819.  *----------------------------------------------------------------------
  1820.  *
  1821.  * TdBackspace --
  1822.  *
  1823.  *    Using mode information from tPtr, output the appropriate
  1824.  *    sequence to backspace over the most recently typed character
  1825.  *    in tPtr's input buffer.  Also remove the character from
  1826.  *    the input buffer.
  1827.  *
  1828.  * Results:
  1829.  *    None.
  1830.  *
  1831.  * Side effects:
  1832.  *    TPtr's input buffer ends up with less characters in it,
  1833.  *    and stuff gets added to the output buffer.
  1834.  *
  1835.  *----------------------------------------------------------------------
  1836.  */
  1837.  
  1838. static void
  1839. TdBackspace(tPtr)
  1840.     register Terminal *tPtr;        /* Terminal to backspace. */
  1841. {
  1842.     if (tPtr->sgttyb.sg_flags & ECHO) {
  1843.     if (tPtr->localMode & LCRTBS) {
  1844.         int count;
  1845.         char c;
  1846.  
  1847.         /*
  1848.          * CRT-style backspacing:  the character can actually be erased.
  1849.          * Figure out how wide the character was, then back over it one
  1850.          * space at a time.  If there's output intervening between us and
  1851.          * the next character to erase, then first re-echo everything.
  1852.          */
  1853.  
  1854.         if (tPtr->lastAddedIn == tPtr->lastHidden) {
  1855.         c = tPtr->ltchars.t_rprntc;
  1856.         if ((c & 0377) == 0377) {
  1857.             c = ltcharsDefault.t_rprntc;
  1858.         }
  1859.         TdEcho(tPtr, c);
  1860.         TdEcho(tPtr, '\n');
  1861.         TdRetypeInput(tPtr, tPtr->lastRemovedIn);
  1862.         tPtr->lastHidden = -1;
  1863.         }
  1864.         c = tPtr->inputBuffer[tPtr->lastAddedIn];
  1865.         if (isprint(c)) {
  1866.         count = 1;
  1867.         } else {
  1868.         int i, pos;
  1869.         char c2;
  1870.  
  1871.         /*
  1872.          * Anything besides a normal printing character is tricky.  Tabs
  1873.          * are particularly nasty.  To figure out how much to erase,
  1874.          * work forwards from a known position, computing the position
  1875.          * of the character just before the one being erased.
  1876.          */
  1877.  
  1878.         i = tPtr->keyIndex;
  1879.         pos = tPtr->keyColumn;
  1880.         while (TRUE) {
  1881.             NEXT(i, tPtr->inBufSize, i);
  1882.             if (i == tPtr->lastAddedIn) {
  1883.             break;
  1884.             }
  1885.             c2 = tPtr->inputBuffer[i];
  1886.             if (isprint(c2)) {
  1887.             pos++;
  1888.             } else if (c2 == '\t') {
  1889.             pos = (pos + 8) & ~07;
  1890.             } else if (((c2 == '\n') && !(tPtr->sgttyb.sg_flags & CRMOD))
  1891.                 || (c2 == 04)) {
  1892.             /* No change to position. */
  1893.             } else if (tPtr->localMode & LCTLECH) {
  1894.             pos += 2;
  1895.             } else if (c2 == '\b') {
  1896.             pos -= 1;
  1897.             }
  1898.         }
  1899.         count = tPtr->column - pos;
  1900.         }
  1901.  
  1902.         for ( ; count > 0; count--) {
  1903.         if (tPtr->localMode & LCRTERA) {
  1904.             TdPutChar(tPtr, '\b');
  1905.             TdPutChar(tPtr, ' ');
  1906.         }
  1907.         TdPutChar(tPtr, '\b');
  1908.         }
  1909.     } else if (tPtr->localMode & LPRTERA) {
  1910.  
  1911.         /*
  1912.          * Hardcopy terminal:  backspace by outputting erased characters
  1913.          * between "\" and "/" delimiters.
  1914.          */
  1915.  
  1916.         if (!(tPtr->flags & BS_IN_PROGRESS)) {
  1917.         TdPutChar(tPtr, '\\');
  1918.         tPtr->flags |= BS_IN_PROGRESS;
  1919.         }
  1920.         TdEcho(tPtr, tPtr->inputBuffer[tPtr->lastAddedIn]);
  1921.     } else {
  1922.  
  1923.         /*
  1924.          * Old-style backspace:  just echo the backspace character.
  1925.          */
  1926.  
  1927.         TdEcho(tPtr, tPtr->sgttyb.sg_erase);
  1928.     }
  1929.     }
  1930.     PREV(tPtr->lastAddedIn, tPtr->inBufSize, tPtr->lastAddedIn);
  1931. }
  1932.  
  1933. /*
  1934.  *----------------------------------------------------------------------
  1935.  *
  1936.  * TdFlushInput --
  1937.  *
  1938.  *    Empty the input buffer associated with a terminal.
  1939.  *
  1940.  * Results:
  1941.  *    None.
  1942.  *
  1943.  * Side effects:
  1944.  *    TPtr's input buffer is cleared.
  1945.  *
  1946.  *----------------------------------------------------------------------
  1947.  */
  1948.  
  1949. static void
  1950. TdFlushInput(tPtr)
  1951.     register Terminal *tPtr;        /* Terminal to flush. */
  1952. {
  1953.     tPtr->lastAddedIn = tPtr->lastRemovedIn = 0;
  1954.     tPtr->lastBreak = tPtr->keyIndex = 0;
  1955.     tPtr->lastHidden = -1;
  1956.     tPtr->keyColumn = tPtr->column;
  1957.     tPtr->flags &= ~(LITERAL_NEXT|BS_IN_PROGRESS);
  1958. }
  1959.  
  1960. /*
  1961.  *----------------------------------------------------------------------
  1962.  *
  1963.  * TdFlushOutput --
  1964.  *
  1965.  *    Empty the output queue for a terminal.
  1966.  *
  1967.  * Results:
  1968.  *    None.
  1969.  *
  1970.  * Side effects:
  1971.  *    The output buffer for tPtr is emptied, and the terminal's raw
  1972.  *    control procedure is invoked to empty any other buffers down
  1973.  *    the line.  The cooked side gets notified that the terminal
  1974.  *    is now writable.
  1975.  *
  1976.  *----------------------------------------------------------------------
  1977.  */
  1978.  
  1979. static void
  1980. TdFlushOutput(tPtr)
  1981.     register Terminal *tPtr;        /* Terminal to flush. */
  1982. {
  1983.     tPtr->lastAddedOut = tPtr->lastRemovedOut = 0;
  1984.     tPtr->outCharsBuffered = 0;
  1985.     (*tPtr->rawProc)(tPtr->rawData, TD_RAW_FLUSH_OUTPUT, 0,
  1986.         (char *) NULL, 0, (char *) NULL);
  1987.     (*tPtr->cookedProc)(tPtr->cookedData, TD_COOKED_WRITES_OK, 0,
  1988.         (char *) NULL, 0, (char *) NULL);
  1989. }
  1990.